package com.ivo.core.hibernate;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.sql.Clob;
import java.sql.SQLException;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.query.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.hibernate5.HibernateTemplate;

import com.ivo.util.ExceptionUtil;

/**
 * <h1>title</h1>
 * <p>
 * content
 * </p>
 * <b>Note:</b>
 *
 * @author nexf
 * @since 2017/3/4
 */
@SuppressWarnings(value = {"unchecked"})
public class HibernateInvoker extends HibernateTemplate {

  private Logger logger = LoggerFactory.getLogger(this.getClass());

  /**
   * 设置查询参数
   *
   * @param queryObject 查询对象
   * @param values 查询参数
   */
  private void applyParameterToQuery(Query queryObject, Object... values) {
    if (values != null) {
      for (int i = 0; i < values.length; i++) {
        Object value = values[i];
        String paramName = i + "";
        applyNamedParameterToQuery(queryObject, paramName, value);
      }
    }
  }

  /**
   * 创建查询对象
   *
   * @param session hibernate会话
   * @param isNative 是否开启本地查询
   * @param queryString 待执行查询语句
   * @param values 参数列表
   * @return Query 查询对象
   */
  private Query createQuery(Session session, boolean isNative, String queryString,
      Object... values) {
    Query queryObject =
        isNative ? session.createNativeQuery(queryString) : session.createQuery(queryString);
    prepareQuery(queryObject);
    applyParameterToQuery(queryObject, values);
    return queryObject;
  }

  /**
   * 进行查询
   *
   * @param isNative 是否开启本地查询
   * @param queryString 待执行查询语句
   * @param values 参数列表
   * @return List<?> 查询结果
   */
  public List<?> find(final boolean isNative, final String queryString, final Object... values) {
    if (StringUtils.isBlank(queryString)) {
      throw new IllegalArgumentException("queryString : " + queryString);
    }
    return executeWithNativeSession(session -> {
      Query queryObject = createQuery(session, isNative, queryString, values);
      return queryObject.list();
    });
  }

  /**
   * 进行查询
   *
   * @param queryString 待执行查询语句
   * @param values 参数列表
   * @return List<?> 查询结果
   */
  public List<?> find(String queryString, Object... values) {
    return find(false, queryString, values);
  }

  /**
   * 进行查询
   *
   * @param entityClass 实体类对象
   * @param queryString 待执行查询语句
   * @param values 参数列表
   * @return List<T> 查询结果
   */
  public <T> List<T> find(Class<T> entityClass, String queryString, Object... values) {
    return (List<T>) find(queryString, values);
  }

  /**
   * 获取唯一对象, 结果不唯一时取第结果集一条记录作为返回结果
   *
   * @param isNative 是否开启本地查询
   * @param queryString 待执行查询语句
   * @param values 参数列表
   * @return Object 查询结果
   */
  public Object findUnique(final boolean isNative, final String queryString,
      final Object... values) {
    return this.executeWithNativeSession(session -> {
      Query queryObject = createQuery(session, isNative, queryString, values);
      try {
        return queryObject.uniqueResult();
      } catch (org.hibernate.NonUniqueResultException e) {
        StringBuilder error = new StringBuilder("query result Not Unique: [")
            .append(queryObject.getQueryString()).append("!");
        for (Object object : values) {
          error.append(object.toString()).append(" ");
        }
        error.append("]");
        logger.error(error.toString());
        return queryObject.list().get(0);
      }
    });
  }

  /**
   * 获取唯一对象, 结果不唯一时取第结果集一条记录作为返回结果
   *
   * @param queryString 待执行查询语句
   * @param values 参数列表
   * @return Object 查询结果
   */
  public Object findUnique(String queryString, Object... values) {
    return findUnique(false, queryString, values);

  }


  /**
   * 获取唯一对象, 结果不唯一时取第结果集一条记录作为返回结果
   *
   * @param entityClass 实体类对象
   * @param queryString 待执行查询语句
   * @param values 参数列表
   * @return T 查询结果
   */
  public <T> T findUnique(Class<T> entityClass, String queryString, Object... values) {
    return (T) findUnique(queryString, values);
  }

  /**
   * 计算语句执行结果的条目数量
   *
   * @param isNative 是否开启本地查询
   * @param queryString 待执行查询语句
   * @param values 参数列表
   * @return Long 统计数量
   */
  public Long count(final boolean isNative, final String queryString, final Object... values) {
    return executeWithNativeSession(session -> {
      StringBuilder sb = new StringBuilder();

      if (isNative) {
        sb.append("select count(*) from (").append(queryString).append(") xc");
      } else {
        String hql = HqlHelper.removeSelect(queryString);
        if (hql == null) {
          return null;
        }
        sb.append("select count(*) ").append(hql);
      }
      Number count = (Number) findUnique(isNative, sb.toString(), values);
      return count == null ? 0L : count.longValue();
    });
  }

  /**
   * 计算语句执行结果的条目数量
   *
   * @param queryString 待执行查询语句
   * @param values 参数列表
   * @return Long 统计数量
   */
  public Long count(final String queryString, final Object... values) {
    return count(false, queryString, values);
  }

  /**
   * 分页查询
   *
   * @param pno 待查询页码
   * @param psize 分页大小
   * @param isNative 是否开启本地查询
   * @param queryString 待执行查询语句
   * @param values 参数列表
   * @return List<?> 查询结果
   */
  public List<?> find(final int pno, final int psize, final boolean isNative,
      final String queryString, final Object... values) {
    if (StringUtils.isBlank(queryString)) {
      throw new IllegalArgumentException("queryString : " + queryString);
    }
    if (pno < 1 || psize < 1) {
      throw new HibernateException("illegal pagination arguments: [" + pno + "," + psize);
    }

    return executeWithNativeSession(session -> {
      Query queryObject = createQuery(session, isNative, queryString, values);
      queryObject.setFirstResult((pno - 1) * psize);
      queryObject.setMaxResults(psize);
      return queryObject.list();
    });
  }

  /**
   * 分页查询
   *
   * @param entityClass 实体类对象
   * @param pno 待查询页码
   * @param psize 分页大小
   * @param queryString 待执行查询语句
   * @param values 参数列表
   * @return List<T> 查询结果
   */
  public <T> List<T> find(Class<T> entityClass, int pno, int psize, String queryString,
      Object... values) {
    return (List<T>) find(pno, psize, false, queryString, values);
  }

  /**
   * 指定条件查询，返回limit个对象
   *
   * @param limit 返回结果数量
   * @param isNative 是否开启本地查询
   * @param queryString 待执行查询语句
   * @param values 参数列表
   * @return List<?> 查询结果
   */
  public List<?> find(int limit, boolean isNative, String queryString, Object... values) {
    return find(1, limit, isNative, queryString, values);
  }

  /**
   * 指定条件查询，返回limit个对象
   *
   * @param entityClass 实体类对象
   * @param limit 返回结果数量
   * @param queryString 待执行查询语句
   * @param values 参数列表
   * @return List<T> 查询结果
   */
  public <T> List<T> find(Class<T> entityClass, int limit, String queryString, Object... values) {
    return find(entityClass, 1, limit, queryString, values);
  }


  public Clob createClob(String value) {
    return Hibernate.getLobCreator(getSessionFactory().getCurrentSession()).createClob(value);
  }

  public String readClob(Clob clob) {
    if (clob == null) {
      return null;
    }
    StringBuilder text = new StringBuilder();
    Reader reader = null;
    try {
      reader = clob.getCharacterStream();
      BufferedReader br = new BufferedReader(reader);
      char[] buf = new char[1024];
      int len = -1;
      while ((len = br.read(buf)) != -1) {
        text.append(buf, 0, len);
      }
    } catch (SQLException | IOException e) {
      ExceptionUtil.error(e);
    }
    return text.toString();
  }

}
